#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;
use File::Basename;
use Getopt::Long;
use Pod::Usage;

my $backup_type=undef;          # Type of Backup to be run (LOG or DB)
my $backup_sid=undef;           # SID of Database
my $generation=undef;           # Flag for generation backups
my $autolog=undef;                      # Flag for logbackups done via autolog into filesystem
my $flag_help;                          # Help Flag


GetOptions("type=s"=>\$backup_type,
                   "sid=s"=>\$backup_sid,
                   "generation"=>\$generation,
                   "autolog"=>\$autolog,
                   "help|?"=>\$flag_help);

&pod2usage(verbose=>2) if ($flag_help); # display usage info if help flag is defined
&pod2usage(verbose=>0,message=>"please supply backup type and SID") unless ($backup_type or $backup_sid); # display usage info if options are missing

my $nsr_path = ( $^O =~ m/Win/) ? 'C:/Legato/nsr' : '/nsr'; # setting networker path depending on OS
my $config_file= $nsr_path.'/res/nsrMaxDB_'.$backup_sid.'.cfg'; # setting config-file to use
my @pass;

my %options=(
                                nr_ignores=>1, # times to ignore LOG run while db backup is running - default is 1 max is 3
                                nr_ignores_force=>'', # force parameter to increase nr_ignores up to a max of 12
                                nr_wait=>2, # wait x * 5min , max. 1 hours - default is 10 minutes - time to wait db backup for running log backup
                                user=>'', # user for connection to database
                                pwd=>'', # password for user
                                dbmcli_bin=>'', # path of dbmcli
                                nsr_env_path=> $nsr_path.'/res', # environment files path
                                maxdb_env=> $nsr_path.'/res/env_'.$backup_sid, # environment file for maxdb
                                maxdb_env_backup=> $nsr_path.'/res/env_'.$backup_type.'_'.$backup_sid, # environment template file for db backup
                                maxdb_env_backup_generation=> $nsr_path.'/res/env_'.$backup_type.'_'.$backup_sid.'_Generation', # environment template file for db-generation backup
                                nsr_log_file=> $nsr_path.'/applogs/nsr_'.$backup_sid.'.log', # networker logfile
                                lck_log_file=> $nsr_path.'/tmp/LOG_'.$backup_sid.'_run.lck',  # lock file for log backup
                                lck_db_file=> $nsr_path.'/tmp/DB_'.$backup_sid.'_run.lck',  # lock file for db backup
                                lck_run_file=> $nsr_path.'/tmp/LOG_'.$backup_sid.'_runs_check.lck', # lock file for log backup counter
                                lck_backup_file=> $nsr_path.'/tmp/'.$backup_type.'_'.$backup_sid.'_run.lck', # lock file for defined backup_type
                                error_nr=>2, # error-code to be returned - default is higher than 0
                                NSR_ENV=>undef, # used for db environment settings
                                NSR_MAST=>undef, # used for db environment settings
                                set_variable_1=>undef, # used for db environment settings
                                nr_backup_medium=>1, # number of pipes used for db-backup,
                                maxdb_conf_check_command=>undef, # command for checking config
                                maxdb_usr_connect=>undef, # user connection string
                                maxdb_util_connect=>undef, # util connection string
                                db_connection=>undef, # combined connection string
                                db_connect=>undef # shot form of connection string

);


############################### SUB's ###############################

# sub for reading of configured parameters in config file; paths are mandatory, retentions are optional
sub read_parameter_file(){

my $read_parameter_file=0; # set default return value for sub
open(PARAMETER_FILE,"<$config_file") or die "could not open parameterfile $config_file: $!"; # open config file
        my @parameters=<PARAMETER_FILE>; # read paramters from configfile
        chomp @parameters; # cut \n
close(PARAMETER_FILE); # close file

foreach (@parameters){ # read each line in config file
        next if ($_ =~ m/^$/ || $_ =~ m/^#/); # skip empty line or line begin with # = comment
        my($key,$value)=split(/\s*=\s*/,$_,2); # split line into key and value, from form key = value
        if ($key ne "pwd"){$value=~ s/\s*#.*//g if ($value =~ m/#/);} # check if line contains comment # - and cut comment only if line does not contain password
        $options{$key}=$value; # set key and value in hash
}
if ((not -e $options{dbmcli_bin}) or (not $options{user}) or (not $options{pwd})){
        &logging ("faulty/missing entries in cfg-file - please check");$options{error_nr} = 5;
        &logging ("-" x 40); # logfile entry spacer line
        exit $options{error_nr} # exit with predefined error_nr 2
}
@pass=unpack("C*",$options{pwd});
foreach (@pass){$_--;}
return $read_parameter_file; # return sub status 0 = ok, 1 = not ok (abort flag)
}

# sub for message logging - messages are print to command line and log file
sub logging {
 my $log_message = join(' ',@_);
 open (LOG,">>$options{nsr_log_file}") or die "could not open log file $options{nsr_log_file}\n"; # open log-file
 print localtime()." ".$log_message."\n";       # print message with timestamp onto STDOUT (into commandline for manual started backups)
 print LOG localtime()." ".$log_message."\n"; # print message with timestamp into log-file
 close (LOG) or die "could not close log file $options{nsr_log_file}\n"; # close log-file
}

# sub for getting and also setting maxdb backup-parameters
sub get_set_db_config(){
$options{maxdb_conf_check_command}= "$options{dbmcli_bin} -d $backup_sid $options{maxdb_usr_connect} dbm_configget"; # get db config
	
my @dbm_config=`$options{maxdb_conf_check_command}`;
chomp @dbm_config;
foreach (@dbm_config){
        next if ($_ =~ m/^$/ || $_ =~ m/^OK/);
        my ($key,$value)=split(/\s*=\s*/,$_,2);
        $options{$key}=$value;
}	
# checking NSR_ENV setting in database
if ($options{NSR_ENV}){&logging("NSR_ENV-Parameter which is set in DB: $options{NSR_ENV}");}
if ((not $options{NSR_ENV}) || (not $options{NSR_ENV}=~ m/$options{maxdb_env}\b/)){
       &logging("NSR_ENV-Parameter which schould be set: $options{maxdb_env}");
       &logging("NSR_ENV which is set does not match - setting parameter");
       &logging(`$options{dbmcli_bin} -d $backup_sid $options{db_connect} dbm_configset -raw NSR_ENV $options{maxdb_env}`);
}
# checking NSR_MAST
if ((not $options{set_variable_1}) || (not $options{set_variable_1}=~ m/NSR_MAST=nsradmin\b/)){
       &logging("Parameter nsrmast not set - setting parameter");
       &logging(`$options{dbmcli_bin} -d $backup_sid $options{db_connect} dbm_configset -raw set_variable_1 NSR_MAST=nsradmin`);
}
# checking backup pipes: more defined, that allowed?
my @devs=`$options{dbmcli_bin} -d $backup_sid $options{db_connect} param_directget MAXBACKUPDEVS`; #get db setting for max backup devices
foreach (@devs){
next if (not $_ =~ /^MAX/);
my ($key,$value)=split(/\t/,$_,2);
&logging("Number of devices= $value");
&logging("number of devs in cfg= $options{nr_backup_medium}");
if ($value<$options{nr_backup_medium}){&logging("check configfile -> more devices than allowed");exit "check config -> more devices than allowed! ";}
}
# checking backup pipes: delete existing pipes
my @medium=`$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_getall`;
chomp @medium;
my @med;
my $pipe;
my $command;
foreach (@medium){
	my ($key,$value);
	next if (not($_ =~ /^NSR_*/));
	if ( $^O =~ m/Win/ ){($key,$value)=split(/\s+/,$_,2);}
	else {($key,$value)=split(/\s*\/\s*/,$_,2);}
	push (@med,$key);
}
foreach (@med){
	&logging("Deleting found NSR-Pipe: $_");
	&logging(`$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_delete "$_"`);
}
# creating new pipes
if (not ($nsr_path =~ m/^\/nsr/)){$pipe='\\\\.\\pipe\\';} else {$pipe='/nsr/pipe/';}
my $i=1;
while ($i<=$options{nr_backup_medium}){
	if ($i==$options{nr_backup_medium}){$command="$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_put NSR_DB_$backup_sid $pipe"."pipe1_DB_$backup_sid".' PIPE DATA 0 8 NO NO "" NSR';}
	$command="$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_put NSR_DB_$backup_sid"."\\P0"."$i $pipe"."pipe"."$i"."_DB_$backup_sid".' PIPE DATA 0 8 NO NO "" NSR';
	&logging("creating new pipe: $command");
	&logging(`$command`);
	$i++;
}
my $log_pipe_command="$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_put NSR_LOG_$backup_sid $pipe"."pipe1_LOG_$backup_sid".' PIPE LOG 0 8 NO NO "" NSR';
&logging("creating new pipe: $log_pipe_command");
&logging(`$log_pipe_command`);
&logging("Get all backup-pipes:");
&logging(`$options{dbmcli_bin} -d $backup_sid $options{db_connect} medium_getall`);

}

############################## MAIN ###############################
if ($generation){$options{maxdb_env_backup}=$options{maxdb_env_backup_generation};}
&logging ("-" x 40); # logfile entry spacer line
if (not $backup_type or (($backup_type  ne "LOG") and ($backup_type ne "DB"))){ #check if input is valid or not
	&logging ("Backup Type not defined - exiting"); # logfile entry
	&logging ("-" x 40); # logfile entry spacer line
	exit $options{error_nr}} # exit with predefined error_nr 2

read_parameter_file(); # read entries from parameter file
my $strg=pack("C*",@pass);
$options{pwd}=reverse(substr(lcfirst($strg),0,length($strg)/2)).lcfirst(reverse(substr($strg,length($strg)/2)));
$options{maxdb_usr_connect} = "-u $options{user},$options{pwd}"; # connection string user,password
$options{maxdb_util_connect} = "-uUTL $options{user},$options{pwd}"; # connection string utility session user,password

umask 0111; # setting umask thus file permission is 666
$options{db_connection} = "$options{maxdb_usr_connect} $options{maxdb_util_connect}"; # putting together whole connection string
$options{db_connect} = "$options{maxdb_usr_connect}"; # putting together whole connection string

get_set_db_config(); # get and set db-config

my $backup_command_type="LOG";
if ($backup_type eq "DB"){$backup_command_type="DATA"}

my $backup_command= "$options{dbmcli_bin} -d $backup_sid $options{db_connection} backup_start NSR_$backup_type"."_"."$backup_sid $backup_command_type"; # putting together backup command

my $backup_command_msg = $backup_command; # backup command for output message
$backup_command_msg =~ s/$options{db_connection} //g; # deleting connection information in backup command message - required for logging

$options{error_nr} = 0; # set error counter for exit
if ($options{nr_ignores} > 3) {$options{nr_ignores}=3;} # check for ignore times of log backup while db backup is running - max. 3 is set

if ($options{nr_ignores_force}){$options{nr_ignores_force} >=12 ? $options{nr_ignores}=12 : $options{nr_ignores}=$options{nr_ignores_force};} # nr_ignores_force used to overwrite nr_ignores

if ($options{nr_wait} > 12) {$options{nr_wait}=12;} # check for wait time max. 1 hour is set


&logging ("start of $backup_type backup run for $backup_sid"); # logfile entry

############################### LOG / DB Backup ###############################

if (-e $options{lck_backup_file}) {	# check if lck_backup_file (backup is running) exists and abort if still running
	&logging ("$backup_type backup still running or is being aborted");
	&logging ("if backup is not running delete '$options{lck_backup_file}'");
	$options{error_nr} = 1141;
}
open (LCK,">$options{lck_backup_file}") or die "could not open lck file $options{lck_backup_file}\n"; # create lck-file

if ($backup_type eq "LOG"){	# check if db backup is running - if not try to start log backup
	if (-e $options{lck_db_file}) {
 		my $runs=0; # setting current runs to 0
 		if (-e $options{lck_run_file}) { # check if log file for log backup counter exist if yes then read counter
  			open (RUNS,"<$options{lck_run_file}") or die "could not open lck file $options{lck_run_file}\n"; # open log file for counter
  			$runs = <RUNS>; # read counter
  			close (RUNS); # close log file
 		}
 		$runs += 1; # setting runs 1 higher
 		open (RUNS,">$options{lck_run_file}") or die "could not open lck file $options{lck_run_file}\n"; # open log file for counter for write
 		&logging ("DB Backup is running ignoring LOG Backup $runs times"); # log files entry
 
 		# check if run higher than allowed ignores - if so abort 
 		if ($runs <= $options{nr_ignores}){ 
   			$options{error_nr} = 0; # counter is lower than allowed - setting error nr to 0 - ignore backup and end script successfull
   		} else { # counter is to high - abort backup
   			&logging ("if DB backup is not running delete '$options{lck_db_file}'"); # log file entry
   			$options{error_nr} = 1140; # setting error nr for script abort
 		}
 		print RUNS "$runs"; # print new counter to log file for counter
 		close (RUNS); # close log file for counter
 		&logging ("end of $backup_type backup run for $backup_sid , RC: $options{error_nr}\n"); # logfile entry
   		exit $options{error_nr}; # exit with error code depending on result
	} 
}

if ($backup_type eq "DB"){	# check if log backup is running and wait specific time
	my $run=1;
	my $waittime=300;
	while (-e $options{lck_log_file} && $run <= $options{nr_wait}) {
 		&logging ("log backup is running - waiting ". ($waittime/60) ." min.");
 		$run++;
 		sleep $waittime;
	}
	if (-e $options{lck_log_file}) { # check if lock file still exist and abort if so else start backup
 		&logging ("wait for ". ($run-1) ." * ". ($waittime/60) ." min. but log backup is still running - aborting db backup");
 		$options{error_nr} = 1140;
	}	
}

if ($options{error_nr}==0){	
	if ($backup_type eq "LOG"){unlink $options{lck_run_file} if (-e $options{lck_run_file});} # deleting run file if available
 	if (copy ($options{maxdb_env_backup},$options{maxdb_env})) { # try to copy env template to env file known by maxdb setting and start backup
  			$! = undef; # unsetting error messages
  			&logging ("copied env-file $options{maxdb_env_backup} to $options{maxdb_env}"); # logfile entry
  			&logging ("starting $backup_type backup of $backup_sid with command: $backup_command_msg"); # logfile entry

  			my $backup_result = `$backup_command`; # running backup command - external usage of dbmcli - getting return message
  			chomp $backup_result; # deleting newline in return message
  			$backup_result =~ s/\s+/ /g; # replace mulitple spaces with one space
 
  			&logging ("$backup_result"); # logfile entry
  
  			$options{error_nr} = 0; # setting error nr to 0
  			if ($backup_result =~ m/error\s-123/) { # check if -123 is in return message - exit with rc 0
   				$backup_result = "no logs for backup available (error -123) thus backup successfull"; # setting result text
  			} elsif (($backup_result =~ m/Returncode\s+0/) or ($backup_result =~ m/OK/)) { # check if RC 0 or "OK" is in return message - exit with rc 0
   				$backup_result = "successfully run $backup_type backup for $backup_sid"; # setting result text
  			} else { # if not RC 0 or -123 then failure and abort
   				$backup_result = "failed to run $backup_type backup for $backup_sid"; # setting result text
   				$options{error_nr} = 2; # setting error nr higher than 0
  			}
  			&logging ("run of dbmcli completed with result: $backup_result"); # logfile entry with result text
 	} else { # failed to copy env template to env file - abort backup
  		&logging ("Failed to copy env-file $options{maxdb_env_backup} to $options{maxdb_env}"); # logfile entry
  		$options{error_nr} = 1; # setting error nr
 	}
	close (LCK); # closing lock file
	unlink $options{lck_backup_file}; # deleting lock file
}
&logging ("end of $backup_type backup run for $backup_sid , RC: $options{error_nr}\n"); # logfile entry
exit $options{error_nr}; # exit with error code depending on result

__END__

=pod

=head1 VERSION 1.0

=head1 NAME nsrMaxDB_backup.pl

=head1 DESCRIPTION

The script is used to backup MaxDB databases (LOG and DB Backup). 
The needed Information are provided via commandline options and parameters from config-file. 
The config-file has to be at following location: 
(Windows: C:\Legato\nsr\res\  -  Unix: /nsr/res) 
and name has to be nsrMaxDB_<SID>.cfg where SID is provided via commandline option.

=head1 SYNOPSIS

B<nsrMaxDB_backup.pl> -t <Backup-Type> -s <SID>

B<nsrMaxDB_backup.pl> -t DB -s <SID> -generation

B<nsrMaxDB_backup.pl> -help

=head1 OPTIONS

=over 4

=item -help      displays help

=back

=cut









